/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package wumpusworld;

import java.awt.Point;
import java.util.ArrayList;
import java.util.Random;

/**
 *
 * @author Macias
 */
public class MapGenerator {

    int[][] globalMap;
    Random rand = new Random();
    static float obstaclesCoverageRatio = 0.08f ;//percentage - how many cells, which are not revealed (are not part of clear path) should have to have obstacle

    static int sizeX=8;
    static int sizeY=8;
    
    public int[][] createMap() {
        //initializing map
        globalMap = new int[sizeX][sizeY];
        //all cells are hidden
        for(int c=0;c<sizeX;c++)
            for(int r=0;r<sizeY;r++)
                globalMap[c][r]=1;
//putting gold in random cell(not neighbour of starting 0,0)
        int[]tmp = drawGoldPos();
        globalMap[tmp[0]][tmp[1]] = (MyCanvas.GOLD|MyCanvas.HIDDEN);
//        System.out.println("gold "+tmp[0]+","+tmp[1]);

//finding way from gold to exit
        boolean stop=false;
        while(!stop){
            int[][]tmpMap=findClearWay(globalMap);
//checkining if path we found does not clrear to many cells
            if(checkIfGood(tmpMap)){
                stop=true;
                for(int c=0;c<sizeX;c++)
                    for(int r=0;r<sizeY;r++)
                        globalMap[c][r]=tmpMap[c][r];
            }
        }
//using the same function to put Wumpus;
        tmp = drawWumpusPos(globalMap);
        globalMap[tmp[0]][tmp[1]] =  globalMap[tmp[0]][tmp[1]]|MyCanvas.WUMPUS;
        globalMap=findClearWayWumpus(globalMap);
 //randomly put obstacles - but not on the way finded in previous step
        globalMap=putObstacles(globalMap);

//put clues(wind sulfur etc)
        globalMap=putClues(globalMap);
//hiding all cells
        for(int c=0;c<globalMap.length;c++)
            for(int r=0;r<globalMap[0].length;r++){
                globalMap[c][r]=globalMap[c][r]|MyCanvas.HIDDEN;
//                globalMap[c][r]=globalMap[c][r]-1;
            }

        
//        for(int c=0;c<globalMap.length;c++){
//            System.out.print("\n");
//            for(int r=0;r<globalMap[0].length;r++){
//                 System.out.print(" "+globalMap[c][r]);
//            }
//        }

        globalMap[0][0]=globalMap[0][0]|MyCanvas.ENTERENCE_LEFT;


//        globalMap = revealRandomCells(globalMap);
//        ArrayList<ArrayList<String>> cos = generateTeachingSet(10, 2);
//        System.out.println("######################");

        return globalMap;
    }

    private int[][] createMapForTeachingSet(){
       //initializing map
        int[][] map = new int[sizeX][sizeY];
        //all cells are hidden
        for(int c=0;c<sizeX;c++)
            for(int r=0;r<sizeY;r++)
                map[c][r]=1;
//putting gold in random cell(not neighbour of starting 0,0)
        int[]tmp = drawGoldPos();
        map[tmp[0]][tmp[1]] = (MyCanvas.GOLD|MyCanvas.HIDDEN);
//        System.out.println("gold "+tmp[0]+","+tmp[1]);

//finding way from gold to exit
        boolean stop=false;
        while(!stop){
            int[][]tmpMap=findClearWay(map);
//checkining if path we found does not clrear to many cells
            if(checkIfGood(tmpMap)){
                stop=true;
                for(int c=0;c<sizeX;c++)
                    for(int r=0;r<sizeY;r++)
                        map[c][r]=tmpMap[c][r];
            }
        }
//using the same function to put Wumpus;
        tmp = drawWumpusPos(map);
        map[tmp[0]][tmp[1]] =  map[tmp[0]][tmp[1]]|MyCanvas.WUMPUS;
        map=findClearWayWumpus(map);
 //randomly put obstacles - but not on the way finded in previous step
        map=putObstacles(map);

//put clues(wind sulfur etc)
        map=putClues(map);
//hiding all cells
        for(int c=0;c<map.length;c++)
            for(int r=0;r<map[0].length;r++){
                map[c][r]=map[c][r]|MyCanvas.HIDDEN;
//                globalMap[c][r]=globalMap[c][r]-1;
            }


//        for(int c=0;c<globalMap.length;c++){
//            System.out.print("\n");
//            for(int r=0;r<globalMap[0].length;r++){
//                 System.out.print(" "+globalMap[c][r]);
//            }
//        }

        map[0][0]=map[0][0]|MyCanvas.ENTERENCE_LEFT;




        return map;
    }

    private int[] drawGoldPos() {

        int[]pos=new int[2];
        pos[0]=rand.nextInt(sizeX);
        pos[1]=rand.nextInt(sizeY);
    //if which is preventing from choosing gold pos next to start pos - if it is so we draw again
        if((pos[0]==0 && pos[1]==0) || (pos[0]==1 && pos[1]==0) || (pos[0]==0 && pos[1]==1) || (pos[0]==1 && pos[1]==1))
            pos=drawGoldPos();

        return pos;
    }

    private int[] drawWumpusPos(int[][] map){
        int[]pos=new int[2];
        pos[0]=rand.nextInt(sizeX);
        pos[1]=rand.nextInt(sizeY);
    //if which is preventing from choosing gold pos next to start pos - if it is so we draw again
        if((pos[0]==0 && pos[1]==0) || (pos[0]==1 && pos[1]==0) || (pos[0]==0 && pos[1]==1) || (pos[0]==1 && pos[1]==1)
                || ((map[pos[0]][pos[1]]&MyCanvas.GOLD)!=0))
            pos=drawWumpusPos(map);  //###########tu powinno byc drawWumpusPos


//            System.out.println("wumpus "+map[pos[0]][pos[1]]);
        return pos;
    }

    private int[][] findClearWay(int[][] map) {
        Point pos = new Point(0,0);
//initialisation
        int[][]myMap = new int[sizeX][sizeY];
        for(int c=0;c<map.length;c++)
            for(int r=0;r<map[0].length;r++){
                myMap[c][r]=map[c][r];
                if(map[c][r]==(MyCanvas.GOLD|MyCanvas.HIDDEN)){
                    pos.x=c;
                    pos.y=r;
                }
            }
//agent is 'moving' toward exit marking his way
        while(pos.x!=0 || pos.y!=0){
            pos=drawNewPos(myMap,pos);
            if(pos!=null)
                myMap[pos.x][pos.y]=0;
            else{
                //reseting map and starting again
                pos = new Point(0,0);
                for(int c=0;c<map.length;c++)
                    for(int r=0;r<map[0].length;r++){
                        myMap[c][r]=map[c][r];
                        if(map[c][r]==(MyCanvas.GOLD|MyCanvas.HIDDEN)){
                            pos.x=c;
                            pos.y=r;
                        }
                    }
            }
        }
        
        return myMap;
    }

    private Point drawNewPos(int[][] myMap, Point pos) {
        if(pos==new Point(1,0) || pos== new Point(0,1))
            return new Point(0,0);

        Point newPos = new Point();
        boolean stop=false;
        int counter = 0;
        while(!stop){
            
            int x = rand.nextInt(100);
            
            if(x<=35) {
                newPos.x=pos.x-1;
                newPos.y=pos.y;
                }
            if(x>35 && x<=70) {
                newPos.x=pos.x;
                newPos.y=pos.y-1;
            }
            if(x>70 && x<=85) {
                newPos.x=pos.x+1;
                newPos.y=pos.y;
            }
            if(x>85) {
                newPos.x=pos.x;
                newPos.y=pos.y+1;
            }

            if(newPos.x>=0 && newPos.x<sizeX && newPos.y>=0 && newPos.y<sizeY)
                if(myMap[newPos.x][newPos.y]==MyCanvas.HIDDEN) stop=true;
            counter++;
            if(counter>100)return null;
        }
        return newPos;
    }

    private boolean checkIfGood(int[][] map) {
        int counter=0;                  //counts how many revealed cells we have
        for(int c=0;c<map.length;c++)
                    for(int r=0;r<map[0].length;r++){
                        if(map[c][r]==0) {
                            counter++;
                        }
                    }

        float param = sizeX*sizeY;
        param = counter/param;
        if(param>0.4) return false; //if over 40% of map is revealed then find another way
                else return true;
    }

    private int[][] putObstacles(int[][] map) {
        
        int[][] myMap = new int[sizeX][sizeY];
         for(int c=0;c<map.length;c++)
            for(int r=0;r<map[0].length;r++){
                myMap[c][r]=map[c][r];
             }
        
        int initFreeCellsNo = 0;
        ArrayList<Point> freeCellCoor = new ArrayList(); //list of coordinates of free cells
        //count how many free cells left
        for(int c=0;c<sizeX;c++){
            for(int r=0;r<sizeY;r++){
                if(myMap[c][r]==MyCanvas.HIDDEN){
                    initFreeCellsNo++;
                    freeCellCoor.add(new Point(c,r));
                }
            }
        }
        float actualFreeCellsNo = initFreeCellsNo;
        Point coor = new Point();
        int r;
        while((actualFreeCellsNo/initFreeCellsNo)>(1-obstaclesCoverageRatio)){
             r= rand.nextInt(freeCellCoor.size());//draw which cell we want to fill in
            coor.x=freeCellCoor.get(r).x;
            coor.y=freeCellCoor.get(r).y;
            myMap[coor.x][coor.y]=myMap[coor.x][coor.y]|MyCanvas.PIT;
            freeCellCoor.remove(r);
            actualFreeCellsNo--;
        }

        return myMap;
    }

    private int[][] putClues(int[][] map) {
         int[][] myMap = new int[sizeX][sizeY];
         for(int c=0;c<map.length;c++)
            for(int r=0;r<map[0].length;r++){
                myMap[c][r]=map[c][r];

             }
         for(int c=0;c<map.length;c++)
            for(int r=0;r<map[0].length;r++){

                if((myMap[c][r]&MyCanvas.PIT)!=0){ //if we found pit putt wind around it
                    if(c-1>=0)myMap[c-1][r]=myMap[c-1][r]|MyCanvas.WIND;
                    if(c+1<sizeX)myMap[c+1][r]=myMap[c+1][r]|MyCanvas.WIND;
                    if(r-1>=0)myMap[c][r-1]=myMap[c][r-1]|MyCanvas.WIND;
                    if(r+1<sizeY)myMap[c][r+1]=myMap[c][r+1]|MyCanvas.WIND;
                }
                if((myMap[c][r]&MyCanvas.WUMPUS)!=0){ //if we wumpus pit putt sulfur around it
                    if(c-1>=0)myMap[c-1][r]=myMap[c-1][r]|MyCanvas.SULFUR;
                    if(c+1<sizeX)myMap[c+1][r]=myMap[c+1][r]|MyCanvas.SULFUR;
                    if(r-1>=0)myMap[c][r-1]=myMap[c][r-1]|MyCanvas.SULFUR;
                    if(r+1<sizeY)myMap[c][r+1]=myMap[c][r+1]|MyCanvas.SULFUR;
                }
            }


         return myMap;
    }

     private int[][] findClearWayWumpus(int[][] map) {
         Point pos = new Point(0,0);
         ArrayList<Point> freeCellsPos = new ArrayList();
//initialisation
        int[][]myMap = new int[sizeX][sizeY];
        for(int c=0;c<map.length;c++)
            for(int r=0;r<map[0].length;r++){
                myMap[c][r]=map[c][r];
                if(map[c][r]==(MyCanvas.WUMPUS|MyCanvas.HIDDEN)){
                    pos.x=c;
                    pos.y=r;
                }
                if(map[c][r]==0 || map[c][r]==(MyCanvas.GOLD|MyCanvas.HIDDEN))freeCellsPos.add(new Point(c,r)); //adding new free cell to list
            }

 //finding nearest free cell from wumpus
        float bestDist=sizeX+sizeY;
        Point tmp = new Point(0,0);
        Point best = new Point(0,0);
        for(int i=0;i<freeCellsPos.size();i++){    //finding nearest free cell from wumpus position
            tmp = freeCellsPos.get(i);
            if((Math.abs(tmp.x-pos.x)+Math.abs(tmp.y-pos.y))<bestDist){ //calculating distance to that cell
                bestDist=(Math.abs(tmp.x-pos.x)+Math.abs(tmp.y-pos.y));

                best.x=tmp.x;
                best.y=tmp.y;
            }
        }
//        System.out.println("best "+best.x+" , "+best.y);
        int Xdist = best.x-pos.x;
        int Ydist = best.y-pos.y;
        int param;
        Point actPos=new Point(pos.x,pos.y);
        while(Math.max(Math.abs(Xdist), Math.abs(Ydist))>0){
            if(Xdist>0){
                if(myMap[actPos.x+1][actPos.y]!=(MyCanvas.GOLD|MyCanvas.HIDDEN))myMap[actPos.x+1][actPos.y]=0;
                actPos.x+=1;
                Xdist-=1;
            }
            if(Xdist<0){
                 if(myMap[actPos.x-1][actPos.y]!=(MyCanvas.GOLD|MyCanvas.HIDDEN))myMap[actPos.x-1][actPos.y]=0;
                actPos.x-=1;
                Xdist+=1;
            }
            if(Ydist>0){
                 if(myMap[actPos.x][actPos.y+1]!=(MyCanvas.GOLD|MyCanvas.HIDDEN))myMap[actPos.x][actPos.y+1]=0;
                actPos.y+=1;
                Ydist-=1;
            }
            if(Ydist<0){
                if(myMap[actPos.x][actPos.y-1]!=(MyCanvas.GOLD|MyCanvas.HIDDEN)) myMap[actPos.x][actPos.y-1]=0;
                actPos.y-=1;
                Ydist+=1;
            }
         }
        
        return myMap;
    }

    public void setXSize(int s){
        this.sizeX=s;
    }
    public void setYSize(int s){
        this.sizeY=s;
    }
    public void setObstaclesCoverageRatio(float r){
        this.obstaclesCoverageRatio = r;
    }

/**
 * function generating list of samples used for teaching network
 * @param sizeOfSamples number of samples you wanto to generate
 * @param sizeOfNeighbourhood how many cells around each cell you want to look
 * @return two array lists (first for wind and pits,second for sulfur and wumpus) list.get(0)-ArrayList of strings desribing pits; list.get(1)-wumpus,
 * each consists of array list of strings desribing each cell
 */
    public ArrayList<ArrayList<String>> generateTeachingSet(int sizeOfSamples,int sizeOfNeighbourhood){

        int sampleNr=0;                     //actual sample number
        int[][] map = this.createMapForTeachingSet();                        //actual map - from this maps we generate samples
//        int[][] map = this.globalMap;                        //ONLY FOR TESTING ###########################
        int newMapParam=0;                                     //parameter which tells if we should generate new map = every 10% of size of samples
        ArrayList<Point> describedCells = new ArrayList();              //list of cells which are used on current map to teach network

        ArrayList<ArrayList<String>> list = new ArrayList();
        ArrayList<String>   pitsDescribtions = new ArrayList();         //list of strings desrcibing pits
        ArrayList<String>   wumpusDescribtions = new ArrayList();       //list of strings describing wumpus

        map = revealRandomCells(map);
        describedCells = checkForCellsToDescribe(map);

        while(sampleNr<sizeOfSamples){
            //we generate new map every 10% of genereates samples
            if(newMapParam>(0.1*sizeOfSamples) || describedCells.size()<=0){
                newMapParam=0;
                map=this.createMapForTeachingSet();
//                map = revealRandomCells(map);
                for(int column = 0; column<map.length; column++)
                    for(int row = 0; row<map[column].length; row++){
                        map[column][row] &= ~MyCanvas.HIDDEN;
                    }
                describedCells = checkForCellsToDescribe(map);
            }
            
            int r = 0; //random number of cell to describe
            if(describedCells.size()>1) //if size <= 1 then r=0;
                r= rand.nextInt(describedCells.size()-1);
            
            if(r<=0)r=0;;

            //filling array list of strings describing pit
            String s = generateString(true,describedCells.get(r),map,sizeOfNeighbourhood);
            pitsDescribtions.add(s);
            //filling array list of strings describing wumpus
            s = generateString(false,describedCells.get(r),map,sizeOfNeighbourhood);
            wumpusDescribtions.add(s);
            
            describedCells.remove(r);

            newMapParam++;
            sampleNr++;
        }

       

        list.add(pitsDescribtions);
        list.add(wumpusDescribtions);

//        for(int n=0;n<=list.get(0).size()-1;n++){
//            System.out.println(""+list.get(0).get(n));
//        }
//        for(int n=0;n<=list.get(0).size()-1;n++){
//            System.out.println(""+list.get(1).get(n));
//        }
        return list;
    }
/**
 * function which reveals number of cells - used to have some data for generating teaching set for neural network
 * @param m map where we will reveal cells
 * @return  maps with cells revealed
 */
    private int[][] revealRandomCells(int[][] m) {

        ArrayList<Point> cellsLoc = new ArrayList();
        int[][]map = new int[sizeX][sizeY];

        for(int c=0;c<m.length;c++){
            for(int r=0;r<m[0].length;r++){
//               if(c==0&&r==0)continue;       //niby komorka 0,0 jest zawsze odkryta - stoi tam agent ale to nie dziala przy uczeniu dlatego zakomentowane
                map[c][r]=m[c][r];
                cellsLoc.add(new Point(c,r));
            }
        }
       
        Random r = new Random();
        int nrOfRevealed = r.nextInt(sizeX*sizeY);  //how many cells we want to reveal
        float ratio = (float)nrOfRevealed/((float)sizeX*(float)sizeY);

        if(ratio<0.10){                     //conditions which ensure uss that teaching sample will be good - it will not have samples with all cells hidden
            int weightedAdd = (int) (0.10 * (sizeX * sizeY));
            nrOfRevealed+=weightedAdd;
        }

        int nrOfCells=0;   //how many cells have been revealed
        while((nrOfCells<=nrOfRevealed)&&cellsLoc.size()>0){
            int nr =r.nextInt(cellsLoc.size());
            map[cellsLoc.get(nr).x][cellsLoc.get(nr).y] =map[cellsLoc.get(nr).x][cellsLoc.get(nr).y]-1;

//            System.out.println(""+cellsLoc.get(nr));

            cellsLoc.remove(nr);
            nrOfCells++;
        }

        return map;
    }
    /**
     * function which checks for cells which can be desribed and used to teach network - cells which are not hidden
     * @param map map for which we check for cells
     * @return list of cells coordinates which can be desribed
     */
    private ArrayList<Point> checkForCellsToDescribe(int[][] map) {

        ArrayList<Point> cells = new ArrayList();
        for(int c=0;c<map.length;c++){
            for(int r=0;r<map[0].length;r++){
//                int tmp = map[c][r] | MyCanvas.HIDDEN;
                if(map[c][r]!=(map[c][r] | MyCanvas.HIDDEN)) {
                    cells.add(new Point(r,c));
                }

            }
        }
//        System.out.println("in checking for cells to describe");
//        for(int c=0;c<map.length;c++){
//            for(int r=0;r<map[0].length;r++){
//                System.out.print("|"+map[c][r]+"|");
//            }
//            System.out.println("");
//        }

        return cells;
    }

    /**
     * function generating string writtend to array list
     * @param isPit true if generating string desribing pit
     * @param cellLocation location of cell you want to desribe
     * @param map map on nwhich you look describing cell
     * @param nSize how many cells around we want to check
     * @return generated string - "type|loc.x|loc|y=value" example
     * "wind-1-1=T" values are T-true;F-false;U-unknown(hidden);N-out of map
     */
    private String generateString(boolean isPit, Point cellLocation, int[][] map,int nSize) {
        String msg = "";

//        System.out.println("checking "+cellLocation + " cell "+map[cellLocation.x][cellLocation.y]);
//        for(int c=0;c<map.length;c++){
//                for(int r=0;r<map[0].length;r++){
//                    System.out.print("|"+map[r][c]+"|");
//                }
//                System.out.println("");
//            }

        if(isPit){
            for(int c=cellLocation.x-nSize;c<=cellLocation.x+nSize;c++)
                for(int r=cellLocation.y-nSize;r<=cellLocation.y+nSize;r++){
                    if(c-cellLocation.x==0 && r-cellLocation.y==0)continue; //dont check cell we are describing
                    if(c>=0 && c<sizeX && r>=0 && r<sizeY){     //cell is on map
                        msg+="wind"+(c-cellLocation.x)+(r-cellLocation.y)+"=";
                        if(map[c][r]==(map[c][r] | MyCanvas.HIDDEN)){       //cell is not revealed
                            msg+="U,";
                        }else{
                            if(map[c][r]==(map[c][r] | MyCanvas.WIND))      //if there is wind put T(true)
                                msg+="T,";
                             else msg+="F,";
                        }
                    }
                    //cell is not on map
                    else{
                        msg+="wind"+(c-cellLocation.x)+(r-cellLocation.y)+"=N,";
                    }
                }
            //checking if there is pit in cell we are describing
            if(map[cellLocation.x][cellLocation.y]==(map[cellLocation.x][cellLocation.y] | MyCanvas.PIT))msg+="pit=T";
            else msg+="pit=F";

        }
        //describing wumpus(sulfur) is similar as describing pit
        else{
           
            for(int c=cellLocation.x-nSize;c<=cellLocation.x+nSize;c++)
                for(int r=cellLocation.y-nSize;r<=cellLocation.y+nSize;r++){
                    if(c-cellLocation.x==0 && r-cellLocation.y==0)continue;
                    if(c>=0 && c<sizeX && r>=0 && r<sizeY){//cell is on map
                        msg+="sulfur"+(c-cellLocation.x)+(r-cellLocation.y)+"=";
                        if(map[c][r]==(map[c][r] | MyCanvas.HIDDEN)){
                            msg+="U,";
                        }else{
                            if(map[c][r]==(map[c][r] | MyCanvas.SULFUR))
                                msg+="T,";
                             else msg+="F,";
                        }
                    }
                    //cell is not on map
                    else{
                        msg+="sulfur"+(c-cellLocation.x)+(r-cellLocation.y)+"=N,";
                    }
                }
            if(map[cellLocation.x][cellLocation.y]==(map[cellLocation.x][cellLocation.y] | MyCanvas.WUMPUS))msg+="wumpus=T";
            else msg+="wumpus=F";

        }
//        System.out.println("loc "+cellLocation);
//        System.out.println(""+msg);
        return msg;
    }

    }
